/**
 *    @file       bencoding.h
 *
 *    @brief      
 *    @details
 *
 *    @version    1.0
 *    @date       01/25/2014 06:30:18 PM
 *
 *    @author     Theophilus (), wangtf418@gmail.com 
 */
#ifndef _BF_BENCODING_H_
#define _BF_BENCODING_H_
#include <string>
#include <vector>
#include <map>
#include <iostream>
#include "comdef.h"
#include "comexception.h"

class bencoding;

class bencoding_excep : public comexception
{
public:
	enum bf_bc_excep_t
	{
		BC_EXCEP_ENCODE = 0,
		BC_EXCEP_DECODE = 1,
		BC_EXCEP_INVALID_TYPE = 2
	};
};

enum bf_bc_var_t
{
	BF_VT_NULL,		//No type
	BF_VT_INT,		//Integer
	BF_VT_STR,		//String
	BF_VT_LST,		//List
	BF_VT_DIC		//Dictionary
};

typedef std::string bf_bc_str_t;
typedef bf_uint64_t bf_bc_int_t;
typedef std::vector<bencoding> bf_bc_lst_t;
typedef std::map<bf_bc_str_t, bencoding> bf_bc_dic_t;

template<typename _Tvar>
bf_bc_var_t bc_var_type(const _Tvar &var) {return BF_VT_NULL;}
bf_bc_var_t bc_var_type(const bf_bc_str_t &);
bf_bc_var_t bc_var_type(const bf_bc_int_t &);
bf_bc_var_t bc_var_type(const bf_bc_lst_t &);
bf_bc_var_t bc_var_type(const bf_bc_dic_t &);

class bencoding
{
public:
	bencoding();
	virtual ~bencoding();

public:
	void clear();

	template<typename _Ty>
	bencoding(const _Ty &r)
	{
		reset();
		assign(r);
	}

	bencoding(const bencoding &r)
	{
		reset();
		if (this != &r)
		{
			assign(r);
		}
	}

	operator bf_bc_str_t&();
	operator bf_bc_int_t();
	operator bf_bc_lst_t&();
	operator bf_bc_dic_t&();

	template<typename _Ty>
	bencoding & operator=(const _Ty &r)
	{
		assign(r);
		return *this;
	}

	bencoding & operator=(const bencoding &r)
	{
		if (this != &r)
		{
			assign(r);
		}
		return *this;
	}

	template<typename _Ty>
	bf_bool_t operator==(const _Ty &r) const
	{
		return ((m_type == bc_var_type(r)) && (*((_Ty*)&m_value) == r));
	}

	bf_bool_t operator==(const bencoding &r) const
	{
		if (this == &r)
		{
			return true;
		}

		if (m_type == r.m_type)
		{
			switch (m_type)
			{
			case BF_VT_STR:
				return *((bf_bc_str_t*)&m_value) == *((bf_bc_str_t*)&r.m_value);
				break;
			case BF_VT_INT:
				return *((bf_bc_int_t*)&m_value) == *((bf_bc_int_t*)&r.m_value);
				break;
			case BF_VT_LST:
				return *((bf_bc_lst_t*)&m_value) == *((bf_bc_lst_t*)&r.m_value);
				break;
			case BF_VT_DIC:
				return *((bf_bc_dic_t*)&m_value) == *((bf_bc_dic_t*)&r.m_value);
				break;
			case BF_VT_NULL:
				return true;
				break;
			default:
				//assert(NULL);
				return true;
				break;
			}
		}
		else
		{
			return false;
		}
	}

	template<typename _Ty>
	bf_bool_t operator!=(const _Ty &r) const
	{
		return !(*this == r);
	}

	//method for list or dictionary type
	bf_bc_var_t type() const;
	bf_uint_t size() const;
	bencoding & operator[](bf_uint_t);					///< get list element
	bencoding & operator[](const bf_char_t *);			///< get dictionary element
	bencoding & operator[](const bf_bc_str_t &);		///< get dictionary element
	bf_uint_t count(const bf_char_t *);					///< get dictionary element count by key, return 1 or 0
	bf_uint_t count(const bf_bc_str_t &);				///< get dictionary element count by key, return 1 or 0

	enum in_formatm_type
	{
		ifmt_cod,	//input fomrat of bencoding
	};

	enum out_formatm_type
	{
		ofmt_cod,	//output format of bencoding
		ofmt_des,	//output format of description
	};

	bencoding & format(in_formatm_type);
	bencoding & format(out_formatm_type);

	friend std::ostream & operator<<(std::ostream &, const bencoding &);
	friend std::istream & operator>>(std::istream &, bencoding &);

	static const bf_char_t *dic_prefix;
	static const bf_char_t *dic_suffix;
	static const bf_char_t *dic_break;
	static const bf_char_t *dic_mapping;
	static const bf_char_t *lst_prefix;
	static const bf_char_t *lst_suffix;
	static const bf_char_t *lst_break;
	static const bf_char_t *str_prefix;
	static const bf_char_t *str_suffix;
	static const bf_char_t *str_tab;

protected:
	void reset();

	template<typename _Ty>
	void assign(const _Ty &r)
	{
		clear();
		m_type = bc_var_type(r);
		new ((void*)&m_value)_Ty(r);
	}

	void assign(const bencoding &r)
	{
		clear();
		if (r.m_type != BF_VT_NULL)
		{
			m_type = r.m_type;
			switch(r.m_type)
			{
			case BF_VT_STR:
				new ((void *)&m_value)bf_bc_str_t(*((bf_bc_str_t*)(&r.m_value)));
				break;
			case BF_VT_INT:
				new ((void *)&m_value)bf_bc_int_t(*((bf_bc_int_t*)(&r.m_value)));
				break;
			case BF_VT_LST:
				new ((void *)&m_value)bf_bc_lst_t(*((bf_bc_lst_t*)(&r.m_value)));
				break;
			case BF_VT_DIC:
				new ((void *)&m_value)bf_bc_dic_t(*((bf_bc_dic_t*)(&r.m_value)));
				break;
			default:
				//assert(NULL);
				break;
			}
		}
	}

private:
	in_formatm_type m_in_format;
	out_formatm_type m_out_format;

	union bf_tagm_value_u	//define this union to get max size between str,int,lst,dic
	{
		bf_char_t m_str[sizeof(bf_bc_str_t)];
		bf_char_t m_int[sizeof(bf_bc_int_t)];
		bf_char_t m_lst[sizeof(bf_bc_lst_t)];
		bf_char_t m_dic[sizeof(bf_bc_dic_t)];
	};

	bf_bc_var_t m_type;
	bf_char_t m_value[sizeof(bf_tagm_value_u)];
	bencoding_excep m_excep;
};

std::ostream & operator<<(std::ostream &, const bencoding &);
std::istream & operator>>(std::istream &, bencoding &);

#endif

